<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<style>
body {
  font-family: Helvetica, arial, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  padding-top: 10px;
  padding-bottom: 10px;
  background-color: white;
  padding: 30px; }

body > *:first-child {
  margin-top: 0 !important; }
body > *:last-child {
  margin-bottom: 0 !important; }

a {
  color: #4183C4; }
a.absent {
  color: #cc0000; }
a.anchor {
  display: block;
  padding-left: 30px;
  margin-left: -30px;
  cursor: pointer;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0; }

h1, h2, h3, h4, h5, h6 {
  margin: 20px 0 10px;
  padding: 0;
  font-weight: bold;
  -webkit-font-smoothing: antialiased;
  cursor: text;
  position: relative; }

h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
  background: url() no-repeat 10px center;
  text-decoration: none; }

h1 tt, h1 code {
  font-size: inherit; }

h2 tt, h2 code {
  font-size: inherit; }

h3 tt, h3 code {
  font-size: inherit; }

h4 tt, h4 code {
  font-size: inherit; }

h5 tt, h5 code {
  font-size: inherit; }

h6 tt, h6 code {
  font-size: inherit; }

h1 {
  font-size: 28px;
  color: black; }

h2 {
  font-size: 24px;
  border-bottom: 1px solid #cccccc;
  color: black; }

h3 {
  font-size: 18px; }

h4 {
  font-size: 16px; }

h5 {
  font-size: 14px; }

h6 {
  color: #777777;
  font-size: 14px; }

p, blockquote, ul, ol, dl, li, table, pre {
  margin: 15px 0; }

hr {
  background: transparent url() repeat-x 0 0;
  border: 0 none;
  color: #cccccc;
  height: 4px;
  padding: 0;
}

body > h2:first-child {
  margin-top: 0;
  padding-top: 0; }
body > h1:first-child {
  margin-top: 0;
  padding-top: 0; }
  body > h1:first-child + h2 {
    margin-top: 0;
    padding-top: 0; }
body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child {
  margin-top: 0;
  padding-top: 0; }

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
  margin-top: 0;
  padding-top: 0; }

h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
  margin-top: 0; }

li p.first {
  display: inline-block; }
li {
  margin: 0; }
ul, ol {
  padding-left: 30px; }

ul :first-child, ol :first-child {
  margin-top: 0; }

dl {
  padding: 0; }
  dl dt {
    font-size: 14px;
    font-weight: bold;
    font-style: italic;
    padding: 0;
    margin: 15px 0 5px; }
    dl dt:first-child {
      padding: 0; }
    dl dt > :first-child {
      margin-top: 0; }
    dl dt > :last-child {
      margin-bottom: 0; }
  dl dd {
    margin: 0 0 15px;
    padding: 0 15px; }
    dl dd > :first-child {
      margin-top: 0; }
    dl dd > :last-child {
      margin-bottom: 0; }

blockquote {
  border-left: 4px solid #dddddd;
  padding: 0 15px;
  color: #777777; }
  blockquote > :first-child {
    margin-top: 0; }
  blockquote > :last-child {
    margin-bottom: 0; }

table {
  padding: 0;border-collapse: collapse; }
  table tr {
    border-top: 1px solid #cccccc;
    background-color: white;
    margin: 0;
    padding: 0; }
    table tr:nth-child(2n) {
      background-color: #f8f8f8; }
    table tr th {
      font-weight: bold;
      border: 1px solid #cccccc;
      margin: 0;
      padding: 6px 13px; }
    table tr td {
      border: 1px solid #cccccc;
      margin: 0;
      padding: 6px 13px; }
    table tr th :first-child, table tr td :first-child {
      margin-top: 0; }
    table tr th :last-child, table tr td :last-child {
      margin-bottom: 0; }

img {
  max-width: 100%; }

span.frame {
  display: block;
  overflow: hidden; }
  span.frame > span {
    border: 1px solid #dddddd;
    display: block;
    float: left;
    overflow: hidden;
    margin: 13px 0 0;
    padding: 7px;
    width: auto; }
  span.frame span img {
    display: block;
    float: left; }
  span.frame span span {
    clear: both;
    color: #333333;
    display: block;
    padding: 5px 0 0; }
span.align-center {
  display: block;
  overflow: hidden;
  clear: both; }
  span.align-center > span {
    display: block;
    overflow: hidden;
    margin: 13px auto 0;
    text-align: center; }
  span.align-center span img {
    margin: 0 auto;
    text-align: center; }
span.align-right {
  display: block;
  overflow: hidden;
  clear: both; }
  span.align-right > span {
    display: block;
    overflow: hidden;
    margin: 13px 0 0;
    text-align: right; }
  span.align-right span img {
    margin: 0;
    text-align: right; }
span.float-left {
  display: block;
  margin-right: 13px;
  overflow: hidden;
  float: left; }
  span.float-left span {
    margin: 13px 0 0; }
span.float-right {
  display: block;
  margin-left: 13px;
  overflow: hidden;
  float: right; }
  span.float-right > span {
    display: block;
    overflow: hidden;
    margin: 13px auto 0;
    text-align: right; }

code, tt {
  margin: 0 2px;
  padding: 0 5px;
  white-space: nowrap;
  border: 1px solid #eaeaea;
  background-color: #f8f8f8;
  border-radius: 3px; }

pre code {
  margin: 0;
  padding: 0;
  white-space: pre;
  border: none;
  background: transparent; }

.highlight pre {
  background-color: #f8f8f8;
  border: 1px solid #cccccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px; }

pre {
  background-color: #f8f8f8;
  border: 1px solid #cccccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px; }
  pre code, pre tt {
    background-color: transparent;
    border: none; }

sup {
    font-size: 0.83em;
    vertical-align: super;
    line-height: 0;
}
* {
	-webkit-print-color-adjust: exact;
}
@media screen and (min-width: 914px) {
    body {
        width: 854px;
        margin:0 auto;
    }
}
@media print {
	table, pre {
		page-break-inside: avoid;
	}
	pre {
		word-wrap: break-word;
	}
}
</style>
<title>Eloquent ORM</title>

</head>
<body>
<h1>Eloquent ORM</h1>

<ul>
<li><a href="#introduction">简介</a></li>
<li><a href="#basic-usage">基本用法</a></li>
<li><a href="#mass-assignment">集体赋值</a></li>
<li><a href="#insert-update-delete">插入、更新、删除</a></li>
<li><a href="#soft-deleting">软删除</a></li>
<li><a href="#timestamps">时间戳</a></li>
<li><a href="#query-scopes">查询范围</a></li>
<li><a href="#relationships">关系</a></li>
<li><a href="#querying-relations">查询关系</a></li>
<li><a href="#eager-loading">预先加载</a></li>
<li><a href="#inserting-related-models">插入相关模型</a></li>
<li><a href="#touching-parent-timestamps">触发父模型时间戳</a></li>
<li><a href="#working-with-pivot-tables">与数据透视表工作</a></li>
<li><a href="#collections">集合</a></li>
<li><a href="#accessors-and-mutators">访问器和调整器</a></li>
<li><a href="#date-mutators">日期调整器</a></li>
<li><a href="#model-events">模型事件</a></li>
<li><a href="#model-observers">模型观察者</a></li>
<li><a href="#converting-to-arrays-or-json">转为数组或JSON</a></li>
</ul>


<p><a name="introduction"></a></p>

<h2>简介</h2>

<p>Laravel 自带的 Eloquent ORM 为您的数据库提供了一个优雅的、简单的 ActiveRecord 实现。每一个数据库的表有一个对应的 "Model" 用来与这张表交互。</p>

<p>在开始之前，确认已在 <code>app/config/database.php</code> 文件中配置好数据库连接。</p>

<p><a name="basic-usage"></a></p>

<h2>基本用法</h2>

<p>首先，创建一个 Eloquent 模型。模型通常在 <code>app/models</code> 目录，但是您可以自由地把它们放在任何地方，只要它能根据您的 <code>composer.json</code> 文件自动加载。</p>

<p><strong>定义一个 Eloquent 模型</strong></p>

<pre><code>class User extends Eloquent {}
</code></pre>

<p>注意我们并没有告诉 Eloquent 我们为 <code>User</code> 模型使用了哪一张表。类名的小写、复数的形式将作为表名，除非它被显式地指定。所以，在这种情况下，Eloquent 将假设 <code>User</code> 模型在 <code>users</code> 表中保存记录。您可以在模型中定义一个 <code>table</code> 属性来指定一个自定义的表名：</p>

<pre><code>class User extends Eloquent {

    protected $table = 'my_users';

}
</code></pre>

<blockquote><p><strong>注意:</strong> Eloquent 将假设每张表有一个名为 <code>id</code> 的主键。您可以定义 <code>primaryKey</code> 属性来覆盖这个约定。同样，您可以定义一个 <code>connection</code> 属性来覆盖在使用这个模型时所用的数据库连接。</p></blockquote>

<p>一旦模型被定义，您可以开始在表中检索和创建记录。注意在默认情况下您将需要在表中定义 <code>updated_at</code> 和 <code>created_at</code> 字段。如果您不希望这些列被自动维护，在模型中设置 <code>$timestamps</code> 属性为 <code>false</code>。</p>

<p><strong>获取所有记录</strong></p>

<pre><code>$users = User::all();
</code></pre>

<p><strong>根据主键获取一条记录</strong></p>

<pre><code>$user = User::find(1);

var_dump($user-&gt;name);
</code></pre>

<blockquote><p><strong>注意:</strong> 所有在 [查询构建器] 中适用的函数在 Eloquent 模型的查询中同样适用。</p></blockquote>

<p><strong>根据主键获取一条记录或者抛出一个异常</strong></p>

<p>有时您可能希望当记录没有被找到时抛出一个异常，允许您使用 <code>App::error</code> 处理器捕捉这些异常并显示404页面。</p>

<pre><code>$model = User::findOrFail(1);

$model = User::where('votes', '&gt;', 100)-&gt;firstOrFail();
</code></pre>

<p>注册错误处理器，请监听 <code>ModelNotFoundException</code>：</p>

<pre><code>use Illuminate\Database\Eloquent\ModelNotFoundException;

App::error(function(ModelNotFoundException $e)
{
    return Response::make('Not Found', 404);
});
</code></pre>

<p><strong>使用 Eloquent 模型查询</strong></p>

<pre><code>$users = User::where('votes', '&gt;', 100)-&gt;take(10)-&gt;get();

foreach ($users as $user)
{
    var_dump($user-&gt;name);
}
</code></pre>

<p>当然，您也可以使用查询构建器的统计函数。</p>

<p><strong>Eloquent 统计</strong></p>

<pre><code>$count = User::where('votes', '&gt;', 100)-&gt;count();
</code></pre>

<p>如果您无法通过通过连贯的接口产生查询，可以使用 <code>whereRaw</code>：</p>

<pre><code>$users = User::whereRaw('age &gt; ? and votes = 100', array(25))-&gt;get();
</code></pre>

<p><a name="mass-assignment"></a></p>

<h2>集体赋值</h2>

<p>当创建一个新的模型，您可以传递属性的数组到模型的构造函数。这些属性将通过集体赋值分配给模型。这是很方便的，但把用户的输入盲目地传给模型可能是一个<strong>严重的</strong>安全问题。如果把用户输入盲目地传递给模型，用户可以自由地修改<strong>任何</strong>或者<strong>全部</strong>模型的属性。基于这个原因，默认情况下所有 Eloquent 模型将防止集体赋值。</p>

<p>首先，在模型中设置 <code>fillable</code> 或 <code>guarded</code> 属性。</p>

<p><code>fillable</code> 属性指定哪些属性可以被集体赋值。这可以在类或接口层设置。</p>

<p><strong>在模型中定义 Fillable 属性</strong></p>

<pre><code>class User extends Eloquent {

    protected $fillable = array('first_name', 'last_name', 'email');

}
</code></pre>

<p>在这个例子中，只有三个被列出的属性可以被集体赋值。</p>

<p><code>fillable</code> 的反义词是 <code>guarded</code>，将做为一个黑名单而不是白名单：</p>

<p><strong>在模型中定义 Guarded 属性</strong></p>

<pre><code>class User extends Eloquent {

    protected $guarded = array('id', 'password');

}
</code></pre>

<p>在这个例子中，<code>id</code> 和 <code>password</code> 属性将<strong>不</strong>被允许集体赋值。所有其他属性将被允许集体赋值。您可以使用 guard 方法阻止<strong>所有</strong>属性被集体赋值：</p>

<p><strong>阻止所有属性集体赋值</strong></p>

<pre><code>protected $guarded = array('*');
</code></pre>

<p><a name="insert-update-delete"></a></p>

<h2>插入、更新、删除</h2>

<p>为了从模型中向数据库中创建一个新的记录，简单地创建一个模型实例并调用 <code>save</code> 函数。</p>

<p><strong>保存一个新的模型</strong></p>

<pre><code>$user = new User;

$user-&gt;name = 'John';

$user-&gt;save();
</code></pre>

<blockquote><p><strong>注意:</strong> 通常您的 Eloquent 模型将有自动递增的键。然而，如果您希望指定您自定义的键，在模型中设置 <code>incrementing</code> 属性为 <code>false</code>。</p></blockquote>

<p>您也可以使用 <code>create</code> 函数在一行代码中保存一个新的模型。被插入的模型实例将从函数中返回。但是，在您这样做之前，您需要在模型中指定 <code>fillable</code> 或者 <code>guarded</code> 属性，因为所有 Eloquent 模型默认阻止集体赋值。</p>

<p><strong>在模型中设置 Guarded 属性</strong></p>

<pre><code>class User extends Eloquent {

    protected $guarded = array('id', 'account_id');

}
</code></pre>

<p><strong>使用模型的 Create 函数</strong></p>

<pre><code>$user = User::create(array('name' =&gt; 'John'));
</code></pre>

<p>为了更新一个模型，您可以检索它，改变一个属性，然后使用 <code>save</code> 函数：</p>

<p><strong>更新一个检索到的模型</strong></p>

<pre><code>$user = User::find(1);

$user-&gt;email = 'john@foo.com';

$user-&gt;save();
</code></pre>

<p>有时您可能希望不仅保存模型，还有它的所有关系。为此，您可以使用 <code>push</code> 函数：</p>

<p><strong>保存一个模型和关系</strong></p>

<pre><code>$user-&gt;push();
</code></pre>

<p>您也可以在一组模型上运行更新：</p>

<pre><code>$affectedRows = User::where('votes', '&gt;', 100)-&gt;update(array('status' =&gt; 2));
</code></pre>

<p>删除一个模型，在实例中调用 <code>delete</code> 函数：</p>

<p><strong>删除一个存在的模型</strong></p>

<pre><code>$user = User::find(1);

$user-&gt;delete();
</code></pre>

<p><strong>根据主键删除一个模型</strong></p>

<pre><code>User::destroy(1);

User::destroy(1, 2, 3);
</code></pre>

<p>当然，您可以在一组模型中运行删除查询：</p>

<pre><code>$affectedRows = User::where('votes', '&gt;', 100)-&gt;delete();
</code></pre>

<p>如果您希望简单的在一个模型中更新时间戳，可以使用 <code>touch</code> 函数：</p>

<p><strong>只更新模型的时间戳</strong></p>

<pre><code>$user-&gt;touch();
</code></pre>

<p><a name="soft-deleting"></a></p>

<h2>软删除</h2>

<p>当软删除一个模型，它并没有真的从数据库中删除。相反，一个 <code>deleted_at</code> 时间戳在记录中被设置。为一个模型开启软删除，在模型中指定 <code>softDelete</code> 属性：</p>

<pre><code>class User extends Eloquent {

    protected $softDelete = true;

}
</code></pre>

<p>为了在您的表中添加一个 <code>deleted_at</code> 字段，您可以在迁移中使用 <code>softDeletes</code> 函数：</p>

<pre><code>$table-&gt;softDeletes();
</code></pre>

<p>现在，当您在一个模型中调用 <code>delete</code> 函数，<code>deleted_at</code> 字段将被设置为当前的时间戳。在使用软删除的模型中查询，被软删除的模型将不被包含进查询结果中。为了强制已删除的模型出现在结果集中，在查询中使用 <code>withTrashed</code> 函数：</p>

<p><strong>强制软删除的模型到结果集中</strong></p>

<pre><code>$users = User::withTrashed()-&gt;where('account_id', 1)-&gt;get();
</code></pre>

<p>如果您希望在结果集中<strong>只</strong>包含软删除的模型，您可以使用 <code>onlyTrashed</code> 函数：</p>

<pre><code>$users = User::onlyTrashed()-&gt;where('account_id', 1)-&gt;get();
</code></pre>

<p>恢复一个已被软删除的记录，使用 <code>restore</code> 函数：</p>

<pre><code>$user-&gt;restore();
</code></pre>

<p>您也可以在查询中使用 <code>restore</code> 函数：</p>

<pre><code>User::withTrashed()-&gt;where('account_id', 1)-&gt;restore();
</code></pre>

<p><code>restore</code>  函数也可以在关系中被使用：</p>

<pre><code>$user-&gt;posts()-&gt;restore();
</code></pre>

<p>如果您希望从数据库中真正删除一个模型，您可以使用 <code>forceDelete</code> 函数：</p>

<pre><code>$user-&gt;forceDelete();
</code></pre>

<p><code>forceDelete</code> 函数也可以在关系中被使用：</p>

<pre><code>$user-&gt;posts()-&gt;forceDelete();
</code></pre>

<p>检测一个给定的模型实例是否被软删除，可以使用 <code>trashed</code> 函数：</p>

<pre><code>if ($user-&gt;trashed())
{
    //
}
</code></pre>

<p><a name="timestamps"></a></p>

<h2>时间戳</h2>

<p>默认情况下，Eloquent 在数据的表中自动地将维护 <code>created_at</code> 和 <code>updated_at</code> 字段。只需简单的添加这些 <code>datetime</code> 字段到表中，Eloquent 将为您做剩余的工作。如果您不希望 Eloquent 维护这些字段，在模型中添加以下属性：</p>

<p><strong>禁止自动时间戳</strong></p>

<pre><code>class User extends Eloquent {

    protected $table = 'users';

    public $timestamps = false;

}
</code></pre>

<p>如果您希望定制时间戳的格式，可以在模型中重写 <code>freshTimestamp</code> 函数：</p>

<p><strong>提供一个定制的时间戳格式</strong></p>

<pre><code>class User extends Eloquent {

    public function freshTimestamp()
    {
        return time();
    }

}
</code></pre>

<p><a name="query-scopes"></a></p>

<h2>查询范围</h2>

<p>范围允许您容易在模型中重用查询逻辑。定义一个范围，简单的用 <code>scope</code> 为模型添加前缀：</p>

<p><strong>定义一个查询范围</strong></p>

<pre><code>class User extends Eloquent {

    public function scopePopular($query)
    {
        return $query-&gt;where('votes', '&gt;', 100);
    }

}
</code></pre>

<p><strong>使用一个查询范围</strong></p>

<pre><code>$users = User::popular()-&gt;orderBy('created_at')-&gt;get();
</code></pre>

<p><a name="relationships"></a></p>

<h2>关系</h2>

<p>当然，您的数据库可能是彼此相关的。比如，一篇博客文章可能有许多评论，或者一个订单与下订单的用户相关。Eloquent 使得管理和处理这些关系变得简单。Laravel 提供了四种类型的关系：
- <a href="#one-to-one">一对一</a>
- <a href="#one-to-many">一对多</a>
- <a href="#many-to-many">多对多</a>
- <a href="#polymorphic-relations">多态关系</a></p>

<p><a name="one-to-one"></a></p>

<h3>一对一</h3>

<p>一个一对一关系是一种非常基础的关系。比如，一个 <code>User</code> 模型可以有一个 Phone`。我们可以在 Eloquent 中定义这个关系：</p>

<p><strong>定义一个一对一关系</strong></p>

<pre><code>class User extends Eloquent {

    public function phone()
    {
        return $this-&gt;hasOne('Phone');
    }

}
</code></pre>

<p>传递给 <code>hasOne</code> 函数的第一个参数是相关模型的名字。一旦这个关系被定义，我们可以使用 Eloquent 的 [动态属性] 获取它：</p>

<pre><code>$phone = User::find(1)-&gt;phone;
</code></pre>

<p>这条语句所产生的 SQL 语句如下:</p>

<pre><code>select * from users where id = 1

select * from phones where user_id = 1
</code></pre>

<p>注意 Eloquent 假设关系的外键基于模型的名字。在这个例子中假设 <code>Phone</code> 模型使用一个 <code>user_id</code> 外键。如果您希望覆盖这个惯例，您可以为传递 <code>hasOne</code> 函数传递第二个参数：</p>

<pre><code>return $this-&gt;hasOne('Phone', 'custom_key');
</code></pre>

<p>在 <code>Phone</code> 模型定义逆向关系，我们使用 <code>belongsTo</code> 函数：</p>

<p><strong>定义一个逆向关系</strong></p>

<pre><code>class Phone extends Eloquent {

    public function user()
    {
        return $this-&gt;belongsTo('User');
    }

}
</code></pre>

<p><a name="one-to-many"></a></p>

<h3>一对多</h3>

<p>一个一对多关系的例子是一篇博客文章有许多评论。我们可以像这样定义关系模型：</p>

<pre><code>class Post extends Eloquent {

    public function comments()
    {
        return $this-&gt;hasMany('Comment');
    }

}
</code></pre>

<p>现在我们可以通过 <a href="#dynamic-properties">动态属性</a> 访问文章的评论：</p>

<pre><code>$comments = Post::find(1)-&gt;comments;
</code></pre>

<p>如果您需要添加进一步的约束检索哪些评论，我们可以调用 <code>comments</code> 函数连接链式条件：</p>

<pre><code>$comments = Post::find(1)-&gt;comments()-&gt;where('title', '=', 'foo')-&gt;first();
</code></pre>

<p>再次，如果您想覆盖默认的外键，可以给 <code>hasMany</code> 函数传递第二个参数：</p>

<pre><code>return $this-&gt;hasMany('Comment', 'custom_key');
</code></pre>

<p>在 <code>Comment</code> 模型中定义逆向关系，我们使用 <code>belongsTo</code> 函数：</p>

<p><strong>定义逆向关系</strong></p>

<pre><code>class Comment extends Eloquent {

    public function post()
    {
        return $this-&gt;belongsTo('Post');
    }

}
</code></pre>

<p><a name="many-to-many"></a></p>

<h3>多对多</h3>

<p>多对多关系更复杂的关系类型。这种关系的一个例子是一个用户和角色，这个角色也可被其他用户共享。比如，许多用户有 "Admin" 的角色。这个关系需要三个表：<code>users</code>, <code>roles</code> 以及 <code>role_user</code>。<code>role_user</code> 表来源于相关的角色名，并且有 <code>user_id</code> 和 <code>role_id</code> 字段。</p>

<p>我们可以使用 <code>belongsToMany</code> 函数定义一个多对多关系：</p>

<pre><code>class User extends Eloquent {

    public function roles()
    {
        return $this-&gt;belongsToMany('Role');
    }

}
</code></pre>

<p>现在，我们可以通过 <code>User</code> 模型获取角色：</p>

<pre><code>$roles = User::find(1)-&gt;roles;
</code></pre>

<p>如果您想使用一个非常规的表名作为关系表，您可以传递它作为第二个参数给 <code>belongsToMany</code> 函数：</p>

<pre><code>return $this-&gt;belongsToMany('Role', 'user_roles');
</code></pre>

<p>您也可以覆盖相关的键：</p>

<pre><code>return $this-&gt;belongsToMany('Role', 'user_roles', 'user_id', 'foo_id');
</code></pre>

<p>当然，您也可以定义在 <code>Role</code> 模型中定义逆向关系：</p>

<pre><code>class Role extends Eloquent {

    public function users()
    {
        return $this-&gt;belongsToMany('User');
    }

}
</code></pre>

<p><a name="polymorphic-relations"></a></p>

<h3>多态关系</h3>

<p>多态关系允许在一个关联中一个模型属于多于一个的其他模型。比如，您可能有一个 photo 模型属于一个员工模型或者一个订单模型。我们可以定义像这样定义这个关系：</p>

<pre><code>class Photo extends Eloquent {

    public function imageable()
    {
        return $this-&gt;morphTo();
    }

}

class Staff extends Eloquent {

    public function photos()
    {
        return $this-&gt;morphMany('Photo', 'imageable');
    }

}

class Order extends Eloquent {

    public function photos()
    {
        return $this-&gt;morphMany('Photo', 'imageable');
    }

}
</code></pre>

<p>现在，我们可以为一个员工或者一个订单获取照片：</p>

<p><strong>获取一个多态关系</strong></p>

<pre><code>$staff = Staff::find(1);

foreach ($staff-&gt;photos as $photo)
{
    //
}
</code></pre>

<p>然而，"polymorphic" 的真正魔力是当您从 <code>Photo</code> 模型中访问员工或者订单的时候：</p>

<p><strong>获取多态关系的属主</strong></p>

<pre><code>$photo = Photo::find(1);

$imageable = $photo-&gt;imageable;
</code></pre>

<p><code>Photo</code> 模型的 <code>imageable</code> 关系将返回一个 <code>Staff</code> 或者 <code>Order</code> 实例，依赖于那种类型的模型拥有这个照片。</p>

<p>帮助理解这是怎样工作的，让我们探讨一个多态关系的数据库结构：</p>

<p><strong>多态关系的数据库结构</strong></p>

<pre><code>staff
    id - integer
    name - string

orders
    id - integer
    price - integer

photos
    id - integer
    path - string
    imageable_id - integer
    imageable_type - string
</code></pre>

<p>这里需要注意的键字段是表 <code>photos</code> 的 <code>imageable_id</code> 和 <code>imageable_type</code> 字段。ID 将包含所属员工或订单的 ID，类型将包含所属模型的类名。当访问 <code>imageable</code> 关系的时候将允许 ORM 检测所属模型的类型。</p>

<p><a name="querying-relations"></a></p>

<h2>查询关系</h2>

<p>当为一个模型获取记录的时候，您可能希望基于一个已存在的关系限制结果集的数目。比如，您希望获取至少有一条评论的文章。为此，您可以使用 <code>has</code> 函数：</p>

<p><strong>查询时检查关系</strong></p>

<pre><code>$posts = Post::has('comments')-&gt;get();
</code></pre>

<p>您也可以指定一个操作符和数量：</p>

<pre><code>$posts = Post::has('comments', '&gt;=', 3)-&gt;get();
</code></pre>

<p><a name="dynamic-properties"></a></p>

<h3>动态属性</h3>

<p>Eloquent 允许您通过动态属性访问关系。Eloquent 将为您自动加载关系，并且足够聪明到知道是否调用 <code>get</code> （为一对多关系）或者 <code>first</code> （为一对一关系）。然后，它将通过动态属性访问作为关系。比如，使用下面的 <code>Phone</code> 模型：</p>

<pre><code>class Phone extends Eloquent {

    public function user()
    {
        return $this-&gt;belongsTo('User');
    }

}

$phone = Phone::find(1);
</code></pre>

<p>不需要像这样打印用户的电子邮件：</p>

<pre><code>echo $phone-&gt;user()-&gt;first()-&gt;email;
</code></pre>

<p>它可以简化为：</p>

<pre><code>echo $phone-&gt;user-&gt;email;
</code></pre>

<p><a name="eager-loading"></a></p>

<h2>预先加载</h2>

<p>预先加载的存在是为了缓解 N + 1 查询问题。比如，考虑一个 <code>Author</code> 相关的 <code>Book</code> 模型。关系定义是这样的：</p>

<pre><code>class Book extends Eloquent {

    public function author()
    {
        return $this-&gt;belongsTo('Author');
    }

}
</code></pre>

<p>现在，考虑下面的代码：</p>

<pre><code>foreach (Book::all() as $book)
{
    echo $book-&gt;author-&gt;name;
}
</code></pre>

<p>此循环将执行一个查询获取表中的所有书籍，然后另一个查询为每本书获取作者。所以，如果我们有 25 本书，这个循环将允许 26 个查询。</p>

<p>值得庆幸的是，我们可以使用预先加载来减少查询的数量。这个关系应该被预先加载通过 <code>with</code> 函数指定：</p>

<pre><code>foreach (Book::with('author')-&gt;get() as $book)
{
    echo $book-&gt;author-&gt;name;
}
</code></pre>

<p>在上面的循环中，只有两个查询被执行：</p>

<pre><code>select * from books

select * from authors where id in (1, 2, 3, 4, 5, ...)
</code></pre>

<p>明智的使用预先加载可以大大提高应用程序的性能。</p>

<p>当然，您可以一次性预先加载多个关系：</p>

<pre><code>$books = Book::with('author', 'publisher')-&gt;get();
</code></pre>

<p>您甚至可以预先加载嵌套关系：</p>

<pre><code>$books = Book::with('author.contacts')-&gt;get();
</code></pre>

<p>在上面的例子中，<code>author</code> 关系将被预先加载，而且作者的 <code>contacts</code> 关系也将被加载。</p>

<h3>预先加载约束</h3>

<p>有时您可能希望预先加载一个关系，但也为预先加载指定一个条件。这里是一个例子：</p>

<pre><code>$users = User::with(array('posts' =&gt; function($query)
{
    $query-&gt;where('title', 'like', '%first%');
}))-&gt;get();
</code></pre>

<p>在这个例子中，我们预先加载用户的文章，但只限于文章的 title 字段中包含单词 "first" 的文章:</p>

<h3>延迟预先加载</h3>

<p>从一个已存在的模型中直接预先加载相关的模型也是可能的。这在动态的决定是否加载相关模型或与缓存结合时可能有用。</p>

<pre><code>$books = Book::all();

$books-&gt;load('author', 'publisher');
</code></pre>

<p><a name="inserting-related-models"></a></p>

<h2>插入相关模型</h2>

<p>您会经常需要插入新的相关模型。比如，你可能希望为一篇文章插入一条新的评论。不需要在模型中手动设置 <code>post_id</code> 外键，您可以直接从它的父模型 <code>Post</code> 中插入新的评论：</p>

<p><strong>附加一个相关的模型</strong></p>

<pre><code>$comment = new Comment(array('message' =&gt; 'A new comment.'));

$post = Post::find(1);

$comment = $post-&gt;comments()-&gt;save($comment);
</code></pre>

<p>在这个例子中，所插入评论的 <code>post_id</code> 字段将自动设置。</p>

<h3>相关模型 (属于)</h3>

<p>当更新一个 <code>belongsTo</code> 关系，您可以使用 <code>associate</code> 函数，这个函数将为子模型设置外键：</p>

<pre><code>$account = Account::find(10);

$user-&gt;account()-&gt;associate($account);

$user-&gt;save();
</code></pre>

<h3>插入相关模型 (多对多)</h3>

<p>当处理多对多关系时，您也可以插入相关模型。让我们继续使用我们的 <code>User</code> 和 <code>Role</code> 模型作为例子。我们能够轻松地使用 <code>attach</code> 函数为一个用户附加新的角色：</p>

<p><strong>附加多对多模型</strong></p>

<pre><code>$user = User::find(1);

$user-&gt;roles()-&gt;attach(1);
</code></pre>

<p>您也可以传递一个属性的数组，存在关系的表中：</p>

<pre><code>$user-&gt;roles()-&gt;attach(1, array('expires' =&gt; $expires));
</code></pre>

<p>当然，<code>attach</code> 的反义词是 <code>detach</code>：</p>

<pre><code>$user-&gt;roles()-&gt;detach(1);
</code></pre>

<p>您也可以使用 <code>sync</code> 函数附加相关的模型。<code>sync</code> 函数接受一个 IDs 数组。当这个操作完成，只有数组中的 IDs 将会为这个模型存在关系表中：</p>

<p><strong>使用 Sync 附加多对多关系</strong></p>

<pre><code>$user-&gt;roles()-&gt;sync(array(1, 2, 3));
</code></pre>

<p>您也可以用给定的 IDs 关联其他关系表中的值:</p>

<p><strong>同步时添加关系数据</strong></p>

<pre><code>$user-&gt;roles()-&gt;sync(array(1 =&gt; array('expires' =&gt; true)));
</code></pre>

<p>有时您可能希望创建一个新的相关的模型，并且在一行中附加它。对于此操作，您可以使用 <code>save</code> 函数：</p>

<pre><code>$role = new Role(array('name' =&gt; 'Editor'));

User::find(1)-&gt;roles()-&gt;save($role);
</code></pre>

<p>在这个例子中，新的 <code>Role</code> 模型将被保存并附加到用户模型。您也可以传递一个属性的数组加入到在这个操作中所连接的表：</p>

<pre><code>User::find(1)-&gt;roles()-&gt;save($role, array('expires' =&gt; $expires));
</code></pre>

<p><a name="touching-parent-timestamps"></a></p>

<h2>触发父模型时间戳</h2>

<p>当一个模型 <code>belongsTo</code> 另一个模型，比如一个 <code>Comment</code> 属于一个 <code>Post</code>，当更新子模型时更新父模型的时间戳通常是有用的。比如，当一个 <code>Comment</code> 模型更新，您想自动更新所属 <code>Post</code> 的 <code>updated_at</code> 时间戳。Eloquent 使之变得容易，只需要在子模型中添加 <code>touches</code> 属性包含关系的名字：</p>

<pre><code>class Comment extends Eloquent {

    protected $touches = array('post');

    public function post()
    {
        return $this-&gt;belongsTo('Post');
    }

}
</code></pre>

<p>现在，当您更新一个 <code>Comment</code>，所属的 <code>Post</code> 的 <code>updated_at</code> 字段也将被更新：</p>

<pre><code>$comment = Comment::find(1);

$comment-&gt;text = 'Edit to this comment!';

$comment-&gt;save();
</code></pre>

<p><a name="working-with-pivot-tables"></a></p>

<h2>与数据透视表工作</h2>

<p>正如您已经了解到，使用多对多关系需要一个中间表的存在。Eloquent 提供了一些非常有用与这个表交互的方式。比如，让我们假设我们的 <code>User</code> 对象有很多相关的 <code>Role</code> 对象，在访问这个关系时，我们可以在这些模型中访问数据透视表：</p>

<pre><code>$user = User::find(1);

foreach ($user-&gt;roles as $role)
{
    echo $role-&gt;pivot-&gt;created_at;
}
</code></pre>

<p>注意每一个我们检索的 <code>Role</code> 模型将自动赋值给一个 <code>pivot</code> 属性。这个属性包含一个代表中间表的模型，并且可以像其他 Eloquent 模型使用。</p>

<p>默认情况下，在 <code>pivot</code> 对象中只有键，如果您的数据透视表包含其他的属性，您必须在定义关系时指定它们：</p>

<pre><code>return $this-&gt;belongsToMany('Role')-&gt;withPivot('foo', 'bar');
</code></pre>

<p>现在，<code>foo</code> 和 <code>bar</code> 属性将可以通过 <code>Role</code> 模型的 <code>pivot</code> 对象中访问。</p>

<p>如果您想您的数据透视表有自动维护的 <code>created_at</code> 和 <code>updated_at</code> 时间戳，在关系定义时使用 <code>withTimestamps</code> 方法：</p>

<pre><code>return $this-&gt;belongsToMany('Role')-&gt;withTimestamps();
</code></pre>

<p>为一个模型在数据透视表中删除所有记录，您可以使用 <code>detach</code> 函数：</p>

<p><strong>在一个数据透视表中删除记录</strong></p>

<pre><code>User::find(1)-&gt;roles()-&gt;detach();
</code></pre>

<p>注意这个操作并不从 <code>roles</code> 删除记录，只从数据透视表中删除。</p>

<p><a name="collections"></a></p>

<h2>集合</h2>

<p>所有通过 <code>get</code> 函数或一个关系返回的多结果集是一个 Eloquent <code>Collection</code> 对象。这个对象实现了 <code>IteratorAggregate</code> PHP 接口，所以可以像数组一样进行遍历。然而，这个对象也有很多其他的函数来处理结果集。</p>

<p>比如，我们可以使用 <code>contains</code> 检测一个结果集是否包含指定的主键：</p>

<p><strong>检测一个集合是否包含一个键</strong></p>

<pre><code>$roles = User::find(1)-&gt;roles;

if ($roles-&gt;contains(2))
{
    //
}
</code></pre>

<p>集合也可以被转换为一个数组或JSON：</p>

<pre><code>$roles = User::find(1)-&gt;roles-&gt;toArray();

$roles = User::find(1)-&gt;roles-&gt;toJson();
</code></pre>

<p>如果一个集合被转换为一个字符转，它将以JSON格式返回：</p>

<pre><code>$roles = (string) User::find(1)-&gt;roles;
</code></pre>

<p>Eloquent 集合也包含一些方法来遍历和过滤所包含的项：</p>

<p><strong>遍历和过滤集合</strong></p>

<pre><code>$roles = $user-&gt;roles-&gt;each(function($role)
{

});

$roles = $user-&gt;roles-&gt;filter(function($role)
{

});
</code></pre>

<p><strong>对每个集合项应用回调函数</strong></p>

<pre><code>$roles = User::find(1)-&gt;roles;

$roles-&gt;each(function($role)
{
    //  
});
</code></pre>

<p><strong>根据一个值排序集合</strong></p>

<pre><code>$roles = $roles-&gt;sortBy(function($role)
{
    return $role-&gt;created_at;
});
</code></pre>

<p>有时，您可能希望返回一个自定义的集合以及自己添加的函数。您可以通过在 Eloquent 模型中重写 <code>newCollection</code> 函数指定这些：</p>

<p><strong>返回一个自定义集合类型</strong></p>

<pre><code>class User extends Eloquent {

    public function newCollection(array $models = array())
    {
        return new CustomCollection($models);
    }

}
</code></pre>

<p><a name="accessors-and-mutators"></a></p>

<h2>访问器和调整器</h2>

<p>Eloquent 为获取和设置模型的属性提供了一种便利的方式。简单的在模型中定义一个 <code>getFooAttribute</code> 函数声明一个访问器。记住，这个函数应该遵循 "camel-casing" 拼写格式，即使您的数据库使用 "snake-case"：</p>

<p><strong>定义一个访问器</strong></p>

<pre><code>class User extends Eloquent {

    public function getFirstNameAttribute($value)
    {
        return ucfirst($value);
    }

}
</code></pre>

<p>在上面的例子中，<code>first_name</code> 字段有一个访问器。注意属性的值被传递到访问器。</p>

<p>调整器以类似的方式声明：</p>

<p><strong>定义一个调整器</strong></p>

<pre><code>class User extends Eloquent {

    public function setFirstNameAttribute($value)
    {
        $this-&gt;attributes['first_name'] = strtolower($value);
    }

}
</code></pre>

<p><a name="date-mutators"></a></p>

<h2>日期调整器</h2>

<p>默认情况下，Eloquent 将转换 <code>created_at</code>、<code>updated_at</code> 以及 <code>deleted_at</code> 字段为 <a href="https://github.com/briannesbitt/Carbon">Carbon</a> 的实例，它提供了各种有用的函数，并且继承自原生 PHP 的 <code>DateTime</code> 类。</p>

<p>您可以指定哪些字段自动调整，设置禁用调整，通过在模型中重写 <code>getDates</code> 函数：</p>

<pre><code>public function getDates()
{
    return array('created_at');
}
</code></pre>

<p>当一个字段被认为是一个日期，您可以设置它的值为一个 UNIX 时间戳，日期字符串 (<code>Y-m-d</code>)，日期时间字符串，当然也可以是一个 <code>DateTime</code> 或 <code>Carbon</code> 实例。</p>

<p>完全禁用日期调整，从 <code>getDates</code> 函数中返回一个空数组：</p>

<pre><code>public function getDates()
{
    return array();
}
</code></pre>

<p><a name="model-events"></a></p>

<h2>模型事件</h2>

<p>Eloquent 模型触发一些事件，允许您使用以下函数在模型的生命周期内的许多地方插入钩子：<code>creating</code>, <code>created</code>, <code>updating</code>, <code>updated</code>, <code>saving</code>, <code>saved</code>, <code>deleting</code>, <code>deleted</code>。如果从 <code>creating</code>, <code>updating</code> 或者 <code>saving</code> 返回 <code>false</code> ，该操作将被取消：</p>

<p><strong>通过事件取消保存操作</strong></p>

<pre><code>User::creating(function($user)
{
    if ( ! $user-&gt;isValid()) return false;
});
</code></pre>

<p>Eloquent 模型也包含一个静态的 <code>boot</code> 函数，它可以提供一个方便的地方注册事件绑定。</p>

<p><strong>设置一个模型 Boot 函数</strong></p>

<pre><code>class User extends Eloquent {

    public static function boot()
    {
        parent::boot();

        // Setup event bindings...
    }

}
</code></pre>

<p><a name="model-observers"></a></p>

<h2>模型观察者</h2>

<p>为加强处理模型事件，您可以注册一个模型观察者。一个观察者类可以包含很多函数对应于很多模型事件。比如，<code>creating</code>, <code>updating</code>, <code>saving</code> 函数可以在一个观察者中，除其他模型事件名之外。</p>

<p>因此，比如，一个模型观察者可以像这样：</p>

<pre><code>class UserObserver {

    public function saving($model)
    {
        //
    }

    public function saved($model)
    {
        //
    }

}
</code></pre>

<p>您可以使用 <code>observe</code> 函数注册一个观察者实例：</p>

<pre><code>User::observe(new UserObserver);
</code></pre>

<p><a name="converting-to-arrays-or-json"></a></p>

<h2>转为数组或JSON</h2>

<p>当构建JSON APIs，您可能经常需要转换您的模型和关系为数组或JSON字符串。所以，Eloquent 包含这些方法。转换一个模型和它加载的关系为一个数组，您可以使用 <code>toArray</code> 函数：</p>

<p><strong>转换一个模型为数组</strong></p>

<pre><code>$user = User::with('roles')-&gt;first();

return $user-&gt;toArray();
</code></pre>

<p>注意模型的全部集合也可以被转为数组：</p>

<pre><code>return User::all()-&gt;toArray();
</code></pre>

<p>转换一个模型为JSON字符串，您可以使用 <code>toJson</code> 函数：</p>

<p><strong>转换一个模型为JSON字符串</strong></p>

<pre><code>return User::find(1)-&gt;toJson();
</code></pre>

<p>注意当一个模型或集合转换为一个字符串，它将转换为JSON，这意味着您可以直接从应用程序的路由中返回 Eloquent 对象！</p>

<p><strong>从路由中返回一个模型</strong></p>

<pre><code>Route::get('users', function()
{
    return User::all();
});
</code></pre>

<p>有时您可能希望限制包含在模型数组或JSON中的属性，比如密码，为此，在模型中添加一个隐藏属性：</p>

<p><strong>从数组或JSON转换中隐藏属性</strong></p>

<pre><code>class User extends Eloquent {

    protected $hidden = array('password');

}
</code></pre>

<p>或者，您可以使用 <code>visible</code> 属性定义一个白名单：</p>

<pre><code>protected $visible = array('first_name', 'last_name');
</code></pre>
</body>
</html>